﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Sofire.DataComm.Net.Async
{

    /// <summary>
    /// 表示关闭套接字所引发的异常。
    /// </summary>
    public class SocketCloseException : Exception
    {
        /// <summary>
        /// 初始化 <see cref="Sofire.DataComm.Net.Async.SocketCloseException"/> 的新实例。
        /// </summary>
        internal SocketCloseException() : base() { }
    }

    /// <summary>
    /// 一个可靠的异步套接字的基类实现。
    /// </summary>
    public abstract class AsyncSocketBase : CommunicationBase
    {
        /// <summary>
        /// 获取默认序列化组件。
        /// </summary>
        public static Sofire.Serialization.FastBinarySerializer DefaultFormatter = new Sofire.Serialization.FastBinarySerializer();
        /// <summary>
        /// 获取消息头的字节大小，该值为 5。消息头包含两样数据，第一字节表示消息的模式（Full 或 Once），剩余为 int 类型值的字节表示。
        /// </summary>
        internal const int DefaultMessageSize = 5;

        /// <summary>
        /// 表示远程套接字关闭的错误。
        /// </summary>
        public static readonly SocketCloseException RemoteSocketCloseException = new SocketCloseException();

        #region Field Area

        private long SendCount = 0;
        private readonly SocketInfo _SocketInfo;
        private readonly string _Name;
        private Encoding _DefaultEncoding = Encoding.UTF8;

        #endregion

        #region Property Area

        private bool _EnabledNoneMode = false;
        /// <summary>
        /// 设置或获取一个值，指示在传输或发送的过程中，是否采用普通模式。默认为 false，表示启用智能模式。
        /// </summary>
        public bool EnabledNoneMode
        {
            get
            {
                return this._EnabledNoneMode;
            }
            set
            {
                this._EnabledNoneMode = value;
            }
        }

        /// <summary>
        /// 获取异步套接字的基础信息。
        /// </summary>
        public SocketInfo SocketInfo
        {
            get
            {
                return this._SocketInfo;
            }
        }

        /// <summary>
        /// 获取异步套接字的唯一标识符。
        /// </summary>
        public string Name
        {
            get
            {
                return this._Name;
            }
        }

        /// <summary>
        /// 获取一个值，指示当前异步套接字是否处于忙碌状态。
        /// </summary>
        public virtual bool IsBusy
        {
            get
            {
                return Interlocked.CompareExchange(ref this.SendCount, 0, 0) != 0L;
            }
        }

        /// <summary>
        /// 获取一个值，指示当前异步套接字正在发送数据的数量。
        /// </summary>
        public long CurrentSendCount
        {
            get
            {
                return Interlocked.CompareExchange(ref this.SendCount, 0, 0);
            }
        }

        /// <summary>
        /// 设置或获取一个值，指示在发送 <see cref="System.String"/> 使用的编码格式。
        /// </summary>
        public Encoding DefaultEncoding
        {
            get
            {
                return this._DefaultEncoding;
            }
            set
            {
                if(value == null) value = Encoding.UTF8;
                this._DefaultEncoding = value;
            }
        }

        #endregion

        #region Event Area

        /// <summary>
        /// 异步套接字抛出异常后发生。
        /// </summary>
        public event AsyncSocketExceptionEventHandler SocketFaild;
        /// <summary>
        /// 异步套接字发送数据后发生。
        /// </summary>
        public event AsyncSocketSendEventHandler AsyncSend;
        /// <summary>
        /// 异步套接字接收数据后发生。
        /// </summary>
        public event AsyncSocketReceiveEventHandler AsyncReceive;

        #endregion

        /// <summary>
        /// 初始化 <see cref="Sofire.DataComm.Net.Async.AsyncSocketBase"/> 的新实例。
        /// </summary>
        /// <param name="name">异步套接字的唯一标识符。</param>
        /// <param name="socketInfo">异步套接字的基础信息。</param>
        public AsyncSocketBase(string name, SocketInfo socketInfo)
        {
            if(socketInfo == null) throw new ArgumentNullException("socketInfo");
            this._Name = name;
            this._SocketInfo = socketInfo;
        }

        #region Method Area

        #region >> Comm <<

        /// <summary>
        /// 异步套接字抛出异常后发生的方法。
        /// </summary>
        /// <param name="asyncEventArgs">当前事件参数关联的 <see cref="System.Net.Sockets.SocketAsyncEventArgs"/>。</param>
        /// <param name="operation">异步套接字操作的类型。</param>
        /// <param name="errorCode">套接字错误代码。</param>
        protected void RaiseSocketFaild(SocketAsyncEventArgs asyncEventArgs
            , SocketOperation operation
            , SocketError errorCode)
        {
            this.RaiseSocketFaild(asyncEventArgs, operation, new SocketException((int)errorCode));
        }

        /// <summary>
        /// 异步套接字抛出异常后发生的方法。
        /// </summary>
        /// <param name="asyncEventArgs">当前事件参数关联的 <see cref="System.Net.Sockets.SocketAsyncEventArgs"/>。</param>
        /// <param name="operation">异步套接字操作的类型。</param>
        /// <param name="exception">抛出的异常信息。</param>
        protected virtual void RaiseSocketFaild(SocketAsyncEventArgs asyncEventArgs
            , SocketOperation operation
            , Exception exception)
        {
            if(this.SocketFaild != null) this.SocketFaild(this, new AsyncSocketExceptionEventArgs(asyncEventArgs, operation, exception));
        }

        /// <summary>
        /// 套接字异步操作完成的方法。
        /// </summary>
        /// <param name="sender">事件对象。</param>
        /// <param name="e">当前事件参数关联的 <see cref="System.Net.Sockets.SocketAsyncEventArgs"/>。</param>
        protected virtual void SocketAsyncCompleted(object sender, SocketAsyncEventArgs e)
        {
            switch(e.LastOperation)
            {
                //case SocketAsyncOperation.Connect:
                //    this.ProcessConnect(e);
                //    break;
#if !SILVERLIGHT
                case SocketAsyncOperation.Accept:
                    this.ProcessAccept(e);
                    break;
#endif
                //case SocketAsyncOperation.ReceiveFrom:
                case SocketAsyncOperation.Receive:
                    this.ProcessReceive(e);
                    break;
                case SocketAsyncOperation.Send:
                    this.ProcessSend(e);
                    break;
                default:
                    throw new ArgumentException("The last operation completed on the socket was not a receive or send");
            }

        }
#if !SILVERLIGHT
        /// <summary>
        /// 开始处理异步接入操作。
        /// </summary>
        /// <param name="e">当前事件参数关联的 <see cref="System.Net.Sockets.SocketAsyncEventArgs"/>。</param>
        protected virtual void ProcessAccept(SocketAsyncEventArgs e) { }
#endif
        //protected virtual void ProcessConnect(SocketAsyncEventArgs e) { }

        #endregion

        #region >> Receive <<

        /// <summary>
        /// 开始处理异步接收操作。
        /// </summary>
        /// <param name="e">当前事件参数关联的 <see cref="System.Net.Sockets.SocketAsyncEventArgs"/>。</param>
        protected virtual void ProcessReceive(SocketAsyncEventArgs e)
        {
            if(!this.IsRunning) return;

            SocketAsyncEventArgsClient e2 = e as SocketAsyncEventArgsClient;
            e2.IsBusy = true;

            var acceptSocket = e2.AcceptSocket;
            var receiveLength = e.BytesTransferred;

            if(e.SocketError != SocketError.Success)
            {
                e2.IsBusy = false;
                this.RaiseSocketFaild(e, SocketOperation.Receive, e.SocketError);
                return;
            }
            else if(receiveLength == 0)
            {
                //客户端已断开
                e2.IsBusy = false;
                this.RaiseSocketFaild(e, SocketOperation.Receive, RemoteSocketCloseException);
                return;
            }

            if(this._EnabledNoneMode)
            {
                this.RaiseNoneReceive(e, e.Buffer, e.Offset, receiveLength);
            }
            else
            {
                var token = e2.BufferHandler;
                if(!token.ProcessReceiveBuffer(e, e.Buffer, e.Offset, receiveLength))
                {
                    e2.IsBusy = false;
                    this.RaiseSocketFaild(e, SocketOperation.Receive, SocketError.AccessDenied);
                    return;
                }
            }

            if(this.IsRunning && acceptSocket.Connected && e2.IsConnected)
            {
                bool async = false;
                try
                {
                    async = acceptSocket.ReceiveAsync(e);
                }
                catch(Exception ex)
                {
                    e2.IsBusy = false;
                    this.RaiseSocketFaild(e, SocketOperation.Receive, ex);
                    return;
                }

                e2.IsBusy = false;
                if(!async) this.ProcessReceive(e);
            }
            else
            {
                e2.IsBusy = false;
                this.RaiseSocketFaild(e, SocketOperation.Receive, RemoteSocketCloseException);
            }

        }

        /// <summary>
        /// 使用 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.Full"/> 处理异步接收的结果。
        /// </summary>
        /// <param name="from">当前事件参数关联的 <see cref="System.Net.Sockets.SocketAsyncEventArgs"/>。</param>
        /// <param name="buffer">异步接收的数据缓冲区。</param>
        /// <param name="offset">数据缓冲区中操作开始位置处的偏移量，以字节为单位。</param>
        /// <param name="count">可在缓冲区中发送或接收的最大数据量（以字节为单位）。</param>
        protected internal virtual void RaiseFullReceive(SocketAsyncEventArgs from, byte[] buffer, int offset, int count)
        {
            if(this.AsyncReceive != null)
            {
                this.RaiseReceive(ReceiveMode.Full, from, buffer, offset, count);
            }
        }

        private IRemote FindRemote(SocketAsyncEventArgsClient eClient)
        {
            IRemote remote = this as IRemote;

            if(remote == null)
            {
                remote = (eClient as IRemoteClientEventArgs).ClientInfo;
            }
            return remote;
        }

        private void RaiseReceive(ReceiveMode mode, SocketAsyncEventArgs from, byte[] buffer, int offset, int count)
        {
            var eClient = from as SocketAsyncEventArgsClient;
            var e = eClient.RaiseReceiveEventArgs;
            e._SocketBase = this;
            e.SetBuffer(mode, buffer, offset, count, this.FindRemote(eClient));
            this.AsyncReceive(this, e);
        }

        /// <summary>
        /// 使用 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.None"/> 处理异步接收的结果。
        /// </summary>
        /// <param name="from">当前事件参数关联的 <see cref="System.Net.Sockets.SocketAsyncEventArgs"/>。</param>
        /// <param name="buffer">异步接收的数据缓冲区。</param>
        /// <param name="offset">数据缓冲区中操作开始位置处的偏移量，以字节为单位。</param>
        /// <param name="count">可在缓冲区中发送或接收的最大数据量（以字节为单位）。</param>
        protected internal virtual void RaiseNoneReceive(SocketAsyncEventArgs from, byte[] buffer, int offset, int count)
        {
            if(this.AsyncReceive != null)
            {
                this.RaiseReceive(ReceiveMode.None, from, buffer, offset, count);
            }
        }

        /// <summary>
        /// 使用 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.Once"/> 处理异步接收的结果。
        /// </summary>
        /// <param name="from">当前事件参数关联的 <see cref="System.Net.Sockets.SocketAsyncEventArgs"/>。</param>
        /// <param name="totalLength">需接收的总长度。</param>
        /// <param name="readLength">已接收的总长度。</param>
        /// <param name="buffer">数据缓冲区。</param>
        /// <param name="offset">数据缓冲区中操作开始位置处的偏移量，以字节为单位。</param>
        /// <param name="count">可在缓冲区中发送或接收的最大数据量（以字节为单位）。</param>
        protected internal virtual void RaiseOnceReceive(SocketAsyncEventArgs from, int totalLength, int readLength, byte[] buffer, int offset, int count)
        {
            if(this.AsyncReceive != null)
            {
                var eClient = from as SocketAsyncEventArgsClient;
                var e = eClient.RaiseReceiveEventArgs;
                e.SetBuffer(ReceiveMode.Once, totalLength, readLength, buffer, offset, count, this.FindRemote(eClient));
                this.AsyncReceive(this, e);
            }
        }

        #endregion

        #region >> Send <<

        /// <summary>
        /// 开始处理异步发送操作。
        /// </summary>
        /// <param name="e">当前事件参数关联的 <see cref="System.Net.Sockets.SocketAsyncEventArgs"/>。</param>
        protected virtual void ProcessSend(SocketAsyncEventArgs e)
        {
            Interlocked.Decrement(ref SendCount);
            if(!this.IsRunning) return;
            var e2 = e as SocketAsyncEventArgsClient;
            var acceptSocket = e2.AcceptSocket;

            var sentLength = e.BytesTransferred;
            if(sentLength == 0)
            {
                this.RaiseSocketFaild(e, SocketOperation.Send, SocketError.ConnectionRefused);
                return;
            }
            else if(e.SocketError != SocketError.Success)
            {
                this.RaiseSocketFaild(e, SocketOperation.Send, e.SocketError);
                return;
            }
            else
            {
                sentLength -= DefaultMessageSize;
            }
            this.RaiseSend(e, sentLength);
        }

        /// <summary>
        /// 异步套接字发送数据后发生。
        /// </summary>
        /// <param name="e">当前事件参数关联的 <see cref="System.Net.Sockets.SocketAsyncEventArgs"/>。</param>
        /// <param name="sendLength">已发送的数据长度。</param>
        protected virtual void RaiseSend(SocketAsyncEventArgs e, int sendLength)
        {
            var eClient = e as SocketAsyncEventArgsClient;
            if(this.AsyncSend != null)
            {
                var sea = eClient.RaiseSendEventArgs;
                sea.AsyncEventArgs = eClient;
                sea.SendLength = sendLength;
                eClient.IsBusy = false;
                this.AsyncSend(this, sea);
            }
            eClient.SetBuffer(null, 0, 0);
            eClient.Set();
        }

        /// <summary>
        /// 指定 <see cref="System.Net.Sockets.SocketAsyncEventArgs"/> 发送数据。
        /// </summary>
        /// <param name="e">当前事件参数关联的 <see cref="System.Net.Sockets.SocketAsyncEventArgs"/>。</param>
        /// <param name="mode">数据接收的模式。</param>
        /// <param name="buffer">要用于异步套接字方法的数据缓冲区。</param>
        /// <param name="bufferOffset">缓冲区的偏移量。</param>
        /// <param name="bufferCount">缓冲区的长度。</param>
        /// <param name="userToken">与发送操作关联的用户自定义数据。</param>
        protected internal virtual void SendTo(SocketAsyncEventArgs e, ReceiveMode mode, byte[] buffer, int bufferOffset, int bufferCount, object userToken)
        {
            if(e == null) throw new ArgumentNullException("e");
            var eClient = (e as SocketAsyncEventArgsClient);
            if(eClient == null) throw new ArgumentException("发送的目标必须是一个 Sofire.DataComm.Net.Async.SocketAsyncEventArgsExts 类型。", "e");

            //- 发送前压缩？

            var remoteSocket = eClient.AcceptSocket;

            if(remoteSocket == null) throw new ArgumentNullException("remoteSocket");


            eClient.WaitOne();

            Interlocked.Increment(ref SendCount);
            eClient.IsBusy = true;

            if(userToken != null) e.UserToken = userToken;

            if(!remoteSocket.Connected || !this.IsRunning)
            {
                this.RaiseSocketFaild(e, SocketOperation.Send, RemoteSocketCloseException);
                goto EndLabel;
            }

            if(buffer == null || bufferCount == 0)
            {
                this.RaiseSend(e, 0);
                goto EndLabel;
            }
            //#if SILVERLIGHT
            //            e.RemoteEndPoint = remoteSocket.RemoteEndPoint;
            //#else
            //            e.AcceptSocket = remoteSocket;
            //#endif
            int sendBufferLength;
            byte[] sendBuffer;
            if(mode == ReceiveMode.None)
            {
                sendBufferLength = bufferCount;
                if(bufferOffset == 0)
                {
                    sendBuffer = buffer;
                }
                else
                {
                    sendBuffer = new byte[sendBufferLength];
                    Buffer.BlockCopy(buffer, bufferOffset, sendBuffer, 0, sendBufferLength);
                }
            }
            else
            {
                sendBufferLength = bufferCount + 5;
                sendBuffer = new byte[sendBufferLength];
                int offset = 0;
                sendBuffer[offset++] = (byte)mode;
                BufferConvert.WriteInt32(bufferCount, sendBuffer, ref offset);

                Buffer.BlockCopy(buffer, bufferOffset, sendBuffer, offset, bufferCount);
            }
            e.SetBuffer(sendBuffer, 0, sendBufferLength);

            bool async = false;
            try
            {
                async = remoteSocket.SendAsync(e);
            }
            catch(Exception ex)
            {
                this.RaiseSocketFaild(e, SocketOperation.Send, ex);
                goto EndLabel;
            }

            if(!async)
            {
                this.ProcessSend(e);
            }
            return;

        EndLabel:
            Interlocked.Decrement(ref SendCount);
        }

        #endregion

        #endregion
    }


}
